// Copyright (c) 2008 Davy Brion (ralinx@davybrion.com)
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
// 
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright notice,
//       this list of conditions and the following disclaimer in the documentation
//       and/or other materials provided with the distribution.
//     * Neither the name of Davy Brion nor the names of the
//       contributors may be used to endorse or promote products derived from this
//       software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

using System;
using System.Collections.Generic;
using NHibernate;
using NHibernate.Criterion;

namespace NHibernate.Servicelayer.NHibernate
{
    public class Repository<T> : IRepository<T>
    {
        private readonly IActiveSessionManager activeSessionManager;

        public Repository(IActiveSessionManager activeSessionManager)
        {
            this.activeSessionManager = activeSessionManager;
        }

        protected ISession Session
        {
            get { return activeSessionManager.GetActiveSession(); }
        }

        public T Get(object id)
        {
            return Session.Get<T>(id);
        }

        public void Delete(T entity)
        {
            Session.Delete(entity);
        }

        public void Delete(DetachedCriteria criteria)
        {
            // a simple DELETE FROM ... WHERE ... would be much better, but i haven't found 
            // a way to do this yet with Criteria. So now it does two roundtrips... one for
            // the query, and one with all the batched delete statements (that is, if you've 
            // enabled CUD statement batching 
            foreach (T entity in FindAll(criteria))
            {
                Delete(entity);
            }
        }

        public void SaveOrUpdate(T entity)
        {
            Session.SaveOrUpdate(entity);
        }

        public IEnumerable<T> FindAll()
        {
            return CriteriaForAll().GetExecutableCriteria(Session).List<T>();
        }

        public IEnumerable<T> FindAll(DetachedCriteria criteria)
        {
            return criteria.GetExecutableCriteria(Session).List<T>();
        }

        public IEnumerable<T> FindAll(DetachedCriteria criteria, params Order[] orders)
        {
            if (orders != null)
            {
                foreach (var order in orders)
                {
                    criteria.AddOrder(order);
                }
            }

            return FindAll(criteria);
        }

        public IEnumerable<T> FindAll(DetachedCriteria criteria, int firstResult, int numberOfResults, params Order[] orders)
        {
            criteria.SetFirstResult(firstResult).SetMaxResults(numberOfResults);
            return FindAll(criteria, orders);
        }

        public T FindOne(DetachedCriteria criteria)
        {
            return criteria.GetExecutableCriteria(Session).UniqueResult<T>();
        }

        public T FindFirst(DetachedCriteria criteria)
        {
            var results = criteria.SetFirstResult(0).SetMaxResults(1)
                .GetExecutableCriteria(Session).List<T>();

            if (results.Count > 0)
            {
                return results[0];
            }

            return default(T);
        }

        public T FindFirst(DetachedCriteria criteria, Order order)
        {
            return FindFirst(criteria.AddOrder(order));
        }

        public long Count(DetachedCriteria criteria)
        {
            return Convert.ToInt64(criteria.GetExecutableCriteria(Session)
                .SetProjection(Projections.RowCountInt64()).UniqueResult());
        }

        public bool Exists(DetachedCriteria criteria)
        {
            return Count(criteria) > 0;
        }

        public DetachedCriteria CriteriaForAll()
        {
            return DetachedCriteria.For<T>();
        }

        protected void ApplyFetchStrategies(DetachedCriteria criteria, params FetchStrategy[] fetchStrategies)
        {
            if (fetchStrategies != null)
            {
                foreach (var fetchStrategy in fetchStrategies)
                {
                    criteria.SetFetchMode(fetchStrategy.Association, fetchStrategy.FetchMode);
                }
            }
        }
    }
}